#include "Actor.h"
#include "RewardOrb.h"
#include "GameApp.h"
#include "GameGUI.h"
#include "MirrorBoss.h"

static const float defaultRes = 100.0f;

/** Bunch of constructors */
//-------------------------------------------------------------------------------------
Actor::Actor(std::string id) : MoveableObject(id)
{
	readActorFromXML(id);
	mHealthCap = mStats->getStat(STAT_HEALTH);
	mEnergyCap = mStats->getStat(STAT_ENERGY);

	resetResources();
	mTypes.push_front(OBJECT_ACTOR);
	
	mStun = 0.f;
	mInvulnerable = 0.f;
	mHitEnemy = false;
}
//-------------------------------------------------------------------------------------
Actor::Actor(boost::shared_ptr<SettingsManager> sm) : MoveableObject(sm)
{
	readActorFromXML(sm);
	mHealthCap = mStats->getStat(STAT_HEALTH);
	mEnergyCap = mStats->getStat(STAT_ENERGY);

	resetResources();
	mTypes.push_front(OBJECT_ACTOR);
	
	mStun = 0.f;
	mInvulnerable = 0.f;
	mHitEnemy = false;
}
//-------------------------------------------------------------------------------------
Actor::~Actor(void)
{
}
//-------------------------------------------------------------------------------------
/* range check */
bool Actor::isInRangeTo(boost::shared_ptr<BaseObject> target)
{
	btVector3 targetPos = target->getBody()->getWorldTransform().getOrigin();
	btVector3 myPos = getBody()->getWorldTransform().getOrigin();

	btScalar range = myPos.distance(targetPos);
	btScalar rangeStat = btScalar(mStats->getStat(STAT_RANGE));

	return !btGreaterEqual(range, rangeStat) || btEqual(range, rangeStat);
}
//-------------------------------------------------------------------------------------
/* read actor data from XML files */
void Actor::readActorFromXML(std::string id)
{
	//load XML file into SettingsManager
	boost::shared_ptr<SettingsManager> sm(new SettingsManager(id));

	//load stats
	boost::shared_ptr<Stats> stats(new Stats(sm));
	mStats = stats;
}
//------------------------------------------------------------------------------------
void Actor::readActorFromXML(boost::shared_ptr<SettingsManager> sm)
{
	//load stats
	boost::shared_ptr<Stats> stats(new Stats(sm));
	mStats = stats;
}
//-------------------------------------------------------------------------------------
/* Getter for all of the current resources (not stats) */
float Actor::getResources(ResourceType type)
{
	switch(type)
	{
		case RES_HEALTH_CAP: return mHealthCap; break;
		case RES_ENERGY_CAP: return mEnergyCap; break;

		case RES_HEALTH: return mHealth; break;
		case RES_ENERGY: return mEnergy; break;

		default: return 0.0f;
	}
}
//-------------------------------------------------------------------------------------
/*Getter for stats */
boost::shared_ptr<Stats> Actor::getStats() {
	return mStats;
}
//-------------------------------------------------------------------------------------
/** 
 * Directly add a specific amount to a specific resource.
 */
bool Actor::changeResources(ResourceType type, float amount)
{
	bool result = false;
	switch(type)
	{
		case RES_HEALTH_CAP: mHealthCap += amount; 
							 result = true;
							 break;

		case RES_ENERGY_CAP: mEnergyCap += amount; 
							 result = true;
							 break;

		case RES_HEALTH: mHealth += amount;
						 if(mHealth > mHealthCap) mHealth = mHealthCap;
						 if(mHealth < 0) mHealth = 0.0f;
						 result = true;
						 break;

		case RES_ENERGY: if(amount < 0.0f && (mEnergy + amount) < 0.0f) //to check energy cost of combo
						 {
							 if(getType() == OBJECT_PLAYER) sysout << "not enough energy." << endl;
							 result = false;
						 } else {
							mEnergy += amount; 
							if(mEnergy > mEnergyCap) mEnergy = mEnergyCap;
							if(mEnergy < 0.0f) mEnergy = 0.0f;
							result = true;
						 }
						 break;

		default: result = false;
				 break;
	}

	this->updateActor();

	return result;
}
//-------------------------------------------------------------------------------------
/** 
 * Directly set a specific resource.
 */
void Actor::setResources(ResourceType res, float amount)
{
	switch(res)
	{
		case RES_HEALTH_CAP: mHealthCap = amount; break;

		case RES_ENERGY_CAP: mEnergyCap = amount; break;

		case RES_HEALTH: mHealth = amount; break;

		case RES_ENERGY: mEnergy = amount; break;

		default: break;
	}

}
//-------------------------------------------------------------------------------------
/**
 * reset resources to default values.
 */
void Actor::resetResources()
{
	mHealth = (float) mHealthCap;
	mEnergy = (float) mEnergyCap;
	
}
//-------------------------------------------------------------------------------------
void Actor::setHitEnemy(const bool hit)
{
	mHitEnemy = hit;
}
//-------------------------------------------------------------------------------------
/**
 * Trigger resources related Events (i.e. Death)
 */
void Actor::updateActor()
{
	//dead?
	if (mHealth <= 0.0f)
	{
		if (getType() == OBJECT_PLAYER)
		{
			if (mMoveState == MOVE_DEATH) return;

			mMoveState = MOVE_DEATH;
			mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Dead"),false, false, 0.5f, 0.0f, 1.0f);
			//gpApp->mpAudio->playSound(SOUND_DEATH, false, shared_from_this());
			return;
		}

		//drop rewards
		createRewardOrb(REW_HEALTH);
		createRewardOrb(REW_ENERGY);
		createRewardOrb(REW_EXP);
		createRewardOrb(REW_SKILL);

		gpApp->mpGameLogic->createParticleSystem(Ogre::String("Death"), 500, cvt(mpBody->getWorldTransform().getOrigin()));
		gpApp->mpAudio->playSound(SOUND_PLAYERMELEE, false, shared_from_this());

		//remove actor from game
		BaseObject::kill();
	}
}
//-------------------------------------------------------------------------------------

void Actor::createRewardOrb(RewardType type)
{
	float rewAmount = this->getStats()->getReward(type);

	//end here if there is no reward.
	if(rewAmount <= 0.0f) return;

	float orbSize = 10.0f;

	//create only one orb if reward amount is below orbSize.
	if(rewAmount <= orbSize)
	{
		orbPtr rOrb = boost::static_pointer_cast<rewardOrb>(gpApp->mpGameLogic->createObject(OBJECT_REWARDORB));

		Ogre::Vector3 &pos = rOrb->getOgreInitData()->pos;
		pos = cvt(this->getBody()->getWorldTransform().getOrigin());
		pos.x = pos.x + rand()/(float)RAND_MAX - 0.5f;
		pos.y = pos.y + rand()/(float)RAND_MAX - 0.5f;
		pos.z = pos.z + rand()/(float)RAND_MAX - 0.5f;

		rOrb->setAmount(rewAmount);
		rOrb->setRewardType(type);

		gpApp->mpGameLogic->addObject(rOrb); 


		return;
	}
	
	//create orbs with 10 points in it.
	for(float i=rewAmount; i <= orbSize; i -= orbSize)
	{
		orbPtr rOrb = boost::static_pointer_cast<rewardOrb>(gpApp->mpGameLogic->createObject(OBJECT_REWARDORB));

		Ogre::Vector3 &pos = rOrb->getOgreInitData()->pos;
		pos = cvt(this->getBody()->getWorldTransform().getOrigin());
		pos.x = pos.x + rand()/(float)RAND_MAX - 0.5f;
		pos.y = pos.y + rand()/(float)RAND_MAX - 0.5f;
		pos.z = pos.z + rand()/(float)RAND_MAX - 0.5f;

		rOrb->setAmount(rewAmount);
		rOrb->setRewardType(type);

		gpApp->mpGameLogic->addObject(rOrb);


		rewAmount = i;
	}

	//create another orb with leftovers
	if(rewAmount > 0)
	{
		orbPtr rOrb = boost::static_pointer_cast<rewardOrb>(gpApp->mpGameLogic->createObject(OBJECT_REWARDORB));

		rOrb->getOgreInitData()->pos = cvt(this->getBody()->getWorldTransform().getOrigin());
		Ogre::Vector3 &pos = rOrb->getOgreInitData()->pos;
		pos = cvt(this->getBody()->getWorldTransform().getOrigin());
		pos.x = pos.x + rand()/(float)RAND_MAX - 0.5f;
		pos.y = pos.y + rand()/(float)RAND_MAX - 0.5f;
		pos.z = pos.z + rand()/(float)RAND_MAX - 0.5f;

		rOrb->setAmount(rewAmount);
		rOrb->setRewardType(type);

		gpApp->mpGameLogic->addObject(rOrb);

	}
}

void Actor::stun(const float dur)
{
	if (mStun < dur)
	{
		mStun = dur;
		mMoveState = MOVE_STUN;
		mCurrentAttack = MOVE_IDLE;
		mStates[MOVE_ATTACK] = false;
	}
}
//-------------------------------------------------------------------------------------
void Actor::knockBack(btVector3 &dir)
{
	if(mpBody)
		mpBody->setLinearVelocity(dir);
}
//-------------------------------------------------------------------------------------
void Actor::updateStun(const unsigned long elapsedTime)
{
	if (mMoveState == MOVE_STUN)
	{
		mStun -= elapsedTime / 1000.0f;
		if (mStun <= 0.0f)
		{
			mStun = 0.f;
			mMoveState = MOVE_IDLE;
		}
	}
}
//-------------------------------------------------------------------------------------
void Actor::updateInvulnerable(const unsigned long elapsedTime)
{
	if (mMoveState == MOVE_DEATH)
	{
		mInvulnerable = 0.0f;
		getEntity()->setVisible(true);
		return;
	}

	mInvulnerable -= elapsedTime / 1000.0f;
	if (mInvulnerable <= 0.0f)
	{
		getEntity()->setVisible(true);
		mInvulnerable = 0.f;
	}
	else getEntity()->setVisible(!getEntity()->getVisible());
}
//-------------------------------------------------------------------------------------
void Actor::recieveDmg(const btVector3 &pos, const float dmg)
{
	if (mInvulnerable == 0.0f) changeResources(RES_HEALTH, -dmg);
}
//-------------------------------------------------------------------------------------
void Actor::scalePower(const char power)
{
}